package stream;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.webrtc.DataChannel;
import org.webrtc.MediaStream;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class RemoteStream {
    private MediaStream ms;
    private DataChannel api;
    private boolean audio;
    private Layer video;
    private Layer frameRate;
    private Layer videoPreMute;

    public RemoteStream(MediaStream stream, DataChannel api) {
        this.ms = stream;
        this.api = api;
    }

    public RemoteStream makeRemote() {
        setAudio(true);
        setFrameRate(Layer.HIGH);
        setVideo(Layer.NONE);
        setFrameRate(Layer.HIGH);
        setVideoPreMute(Layer.HIGH);
        select(createCallFromProperties());
        return this;
    }

    private void select(Call call) {
        ObjectMapper objectMapper = new ObjectMapper();
        if (this.api != null && DataChannel.State.OPEN == this.api.state()) {
            try {
                String json = objectMapper.writeValueAsString(call);
                ByteBuffer byteBuffer = ByteBuffer.wrap(json.getBytes(StandardCharsets.UTF_8));
                DataChannel.Buffer buffer = new DataChannel.Buffer(byteBuffer, true);
                this.api.send(buffer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void preferLayer(Layer layer) {
        this.setVideo(layer);
        select(createCallFromProperties());
    }

    public void preferFrameRate(Layer layer) {
        preferFrameRate(layer);
        select(createCallFromProperties());
    }


    public void mute(Kind kind) {
        if (Kind.AUDIO == kind) {
            setAudio(false);
        } else if (Kind.VIDEO == kind) {
            setVideoPreMute(getVideo());
            setVideo(Layer.NONE);
        }
        select(createCallFromProperties());
    }

    public void unmute(Kind kind) {
        if (Kind.AUDIO == kind) {
            setAudio(true);
        } else if (Kind.VIDEO == kind) {
            setVideo(getVideoPreMute());
        }
        select(createCallFromProperties());
    }

    private Call createCallFromProperties() {
        Call call = new Call();
        call.setStreamId(ms.getId());
        call.setAudio(isAudio());
        call.setVideo(getVideo());
        call.setFrameRate(getFrameRate());
        return call;
    }

    static class Call {
        private String streamId;
        private Layer video;
        private boolean audio;
        private Layer frameRate;

        public Call() {
        }

        public String getStreamId() {
            return streamId;
        }

        public void setStreamId(String streamId) {
            this.streamId = streamId;
        }

        public Layer getVideo() {
            return video;
        }

        public void setVideo(Layer video) {
            this.video = video;
        }

        public boolean isAudio() {
            return audio;
        }

        public void setAudio(boolean audio) {
            this.audio = audio;
        }

        public Layer getFrameRate() {
            return frameRate;
        }

        public void setFrameRate(Layer frameRate) {
            this.frameRate = frameRate;
        }
    }

    public MediaStream getRemoteStream() {
        return this.ms;
    }

    public MediaStream getMs() {
        return ms;
    }

    public void setMs(MediaStream ms) {
        this.ms = ms;
    }

    public DataChannel getApi() {
        return api;
    }

    public void setApi(DataChannel api) {
        this.api = api;
    }

    public boolean isAudio() {
        return audio;
    }

    public void setAudio(boolean audio) {
        this.audio = audio;
    }

    public Layer getFrameRate() {
        return frameRate;
    }

    public void setFrameRate(Layer frameRate) {
        this.frameRate = frameRate;
    }

    public Layer getVideoPreMute() {
        return videoPreMute;
    }

    public void setVideoPreMute(Layer videoPreMute) {
        this.videoPreMute = videoPreMute;
    }

    public Layer getVideo() {
        return video;
    }

    public void setVideo(Layer video) {
        this.video = video;
    }
}
